﻿namespace JsonComaprer.NET
{
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;

    /// <summary>
    /// 比较两个json 字符串或者json对象是否一样
    /// 1. key的数量是否一样。 比如多一个key或者少一个
    /// 2. key如果相同， 比较每个可以对应的 value 是否一样。 key和value比较时，忽略大小写
    /// </summary>
    public sealed class JsonComparer
    {
        public JsonComparerSetting JsonComparerSetting { get;private set; }
        public JsonComparer(JsonComparerSetting JsonComparerSetting)
        {
            this.JsonComparerSetting = JsonComparerSetting;
        }
        

        /// <summary>
        /// 比较2个json对象是否一样
        /// </summary>
        /// <param name="First">第一个JObject</param>
        /// <param name="Second">第二个JObject</param>
        /// <returns>
        /// 返回对象：
        /// Key： 不相同值的key
        /// FirstValue: 第一个对象 这个Key所在的 值
        /// SecondValue: 第二个对象 这个Key所在的 值
        /// </returns>
        public List<CompareResult> IsTheSameJsonObject(JObject First, JObject Second)
        {
            if (First == null || Second == null)
            {
                return null;
            }

            List<CompareResult> tmpResults = _CompareJsonObject(First, Second, String.Empty);

            return tmpResults;
        }

        /// <summary>
        /// 比较2个json 字符串 是否一样
        /// </summary>
        /// <param name="FirstJsonString">第一个json字符串</param>
        /// <param name="SecondJsonString">第二个json字符串</param>
        /// <returns>
        /// 返回对象：
        /// Key： 不相同值的key
        /// FirstValue: 第一个对象 这个Key所在的 值
        /// SecondValue: 第二个对象 这个Key所在的 值
        /// </returns>
        public List<CompareResult> IsTheSameJsonString(String FirstJsonString, String SecondJsonString)
        {
            List<CompareResult> tmpResults = new List<CompareResult>();
            if (String.IsNullOrEmpty(FirstJsonString) && String.IsNullOrEmpty(SecondJsonString))
            {
                return null;
            }
            //一个空字符串， 一个非空，当然不一样
            if ((!String.IsNullOrEmpty(FirstJsonString) && String.IsNullOrEmpty(SecondJsonString)) || (String.IsNullOrEmpty(FirstJsonString) && !String.IsNullOrEmpty(SecondJsonString)))
            {
                tmpResults.Add(new CompareResult() { Key = "The whole json strings are different", FirstValue = FirstJsonString, SecondValue = SecondJsonString });
                return tmpResults;
            }

            JObject tmpFirst = _ReadFromJsonStringToJObject(FirstJsonString);
            JObject tmpSecond = _ReadFromJsonStringToJObject(SecondJsonString);
            if (tmpFirst == null || tmpSecond == null)
            {
                return null;
            }

            tmpResults = _CompareJsonObject(tmpFirst, tmpSecond, String.Empty);
            return tmpResults;
        }

        /// <summary>
        /// 比较2个JObject对象是否一样
        /// </summary>
        /// <param name="First">第一个JObject对象</param>
        /// <param name="Second">第二个JObject对象</param>
        /// <param name="key">这2个JObject 所在key</param>
        /// <returns>
        /// Key： 不相同值的key
        /// FirstValue: 第一个对象 这个Key所在的 值
        /// SecondValue: 第二个对象 这个Key所在的 值
        /// </returns>
        List<CompareResult> _CompareJsonObject(JObject First, JObject Second, String key)
        {
            List<CompareResult> tmpResults = new List<CompareResult>();
            if ((First == null && Second != null) || (First != null && Second == null))
            {
                tmpResults.Add(new CompareResult(key, First != null ? First.ToString() : "", Second != null ? Second.ToString() : ""));
                return tmpResults;
            }
            foreach (KeyValuePair<string, JToken> KeyValue in First)
            {
                var tmp = _CompareObject(KeyValue.Value, Second.GetValue(KeyValue.Key), KeyValue.Key);
                if (tmp != null)
                {
                    tmpResults.AddRange(tmp);
                }
            }
            return tmpResults;
        }

        /// <summary>
        /// 比较任意2个json 对象是否相同
        /// </summary>
        /// <param name="First">第一个json 对象</param>
        /// <param name="Second">第二个json对象</param>
        /// <param name="key">这2个json对象所在的key</param>
        /// <returns>
        /// Key： 不相同值的key
        /// FirstValue: 第一个对象 这个Key所在的 值
        /// SecondValue: 第二个对象 这个Key所在的 值
        /// </returns>
        List<CompareResult> _CompareObject(Object First, Object Second, String key)
        {
            List<CompareResult> tmpResults = new List<CompareResult>();
            if ((First != null && Second == null) || (First == null && Second != null))
            {
                tmpResults.Add(new CompareResult(key, First != null ? First.ToString() : "", Second != null ? Second.ToString() : ""));
                return tmpResults;
            }

            if (First is JObject)
            {
                var tmp = _CompareJsonObject((JObject)First, (JObject)Second, key);
                if (tmp != null)
                {
                    tmpResults.AddRange(tmp);
                }
            }
            else if (First is JArray)
            {
                var tmp = _CompareJson((JArray)First, (JArray)Second, key);
                if (tmp != null)
                {
                    tmpResults.AddRange(tmp);
                }
            }
            else if (First is String)
            {
                var tmp = _CompareJsonString((String)First, (String)Second, key);
                if (tmp != null)
                {
                    tmpResults.AddRange(tmp);
                }
            }
            else
            {
                var tmp = _CompareJsonString(First.ToString(), Second.ToString(), key);
                if (tmp != null)
                {
                    tmpResults.AddRange(tmp);
                }
            }
            return tmpResults;
        }

        /// <summary>
        /// 单纯的比较2个json value 是否一样， 忽略大小写比较
        /// </summary>
        /// <param name="str1">第一个json value</param>
        /// <param name="str2">第二个json value</param>
        /// <param name="key">这2个json value所对应的 json key</param>
        /// <returns>
        /// Key： 不相同值的key
        /// FirstValue: 第一个对象 这个Key所在的 值
        /// SecondValue: 第二个对象 这个Key所在的 值
        /// </returns>
        public List<CompareResult> _CompareJsonString(String str1, String str2, String key)
        {
            List<CompareResult> tmpResults = new List<CompareResult>();
            EqualsMappingSetting tmpSetting = null;
            bool flag=_CanUseEquesMappings(key, out tmpSetting);
            if (tmpResults.Count == 0)
            {
                if (!tmpSetting.IsTheSameValues(str1, str2))
                {
                    tmpResults.Add(new CompareResult(key, str1, str2));
                }
            }

            if (flag)
            {
                if (!((tmpSetting.HasLeftValuesContains(str1) && tmpSetting.HasRightValuesContains(str2)) || (tmpSetting.HasLeftValuesContains(str2) && tmpSetting.HasRightValuesContains(str1))))
                {
                    tmpResults.Add(new CompareResult(key, str1, str2));
                }
                else
                {
                    tmpResults.Clear();
                }
            }
 
            return tmpResults;
        }

        /// <summary>
        /// 判断是否有配置 可以使用。
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Value"></param>
        /// <returns></returns>
        bool _CanUseEquesMappings(String Key, out EqualsMappingSetting Value)
        {
            Value = new EqualsMappingSetting();
            if (this.JsonComparerSetting.EquesMappings != null && this.JsonComparerSetting.EquesMappings.Count == 0)
            {
                return false;
            }
            bool HasSuccessed = this.JsonComparerSetting.EquesMappings.TryGetValue(Key, out Value);
            if (!HasSuccessed || Value == null)
            {
                return false;
            }
            if (Value != null)
            {
                return Value.CanUseEquesMappings();
            }
            return false;
        }

        /// <summary>
        /// 比较2个json 数组 是否一样
        /// </summary>
        /// <param name="First">第一个json 数组</param>
        /// <param name="Second">第二个json 数组</param>
        /// <param name="key">这2个json 数组所在的json key</param>
        /// <returns>
        /// Key： 不相同值的key
        /// FirstValue: 第一个对象 这个Key所在的 值
        /// SecondValue: 第二个对象 这个Key所在的 值
        /// </returns>
        List<CompareResult> _CompareJson(JArray First, JArray Second, String key)
        {
            List<CompareResult> tmpResults = new List<CompareResult>();

            if ((First == null && Second != null) || (First != null && Second == null))
            {
                tmpResults.Add(new CompareResult(key, First != null ? First.ToString() : "", Second != null ? Second.ToString() : ""));
                return tmpResults;
            }

            if (First != null && Second != null && First.Count != Second.Count)
            {
                tmpResults.Add(new CompareResult(key, "count=" + First.Count, "count=" + Second.Count));
                return tmpResults;
            }
            for (var i = 0; i <= First.Count - 1; i++)
            {
                List<CompareResult> tmp = _CompareObject(First.ElementAt(i), Second.ElementAt(i), key);
                if (tmp != null)
                {
                    tmpResults.AddRange(tmp);
                }
            }
            return tmpResults;
        }

        /// <summary>
        /// 将json 字符串 转化为 JObject
        /// </summary>
        /// <param name="JsonString">json 字符串</param>
        /// <returns>
        /// JObject 实例
        /// </returns>
        JObject _ReadFromJsonStringToJObject(String JsonString)
        {
            if (String.IsNullOrEmpty(JsonString))
            {
                return null;
            }
            try
            {
                return JObject.Parse(JsonString.Replace("\r\n", "").Replace("\n", ""), new JsonLoadSettings());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }
    }
}